<?php
class wechatpay
{
    const DEBUG = 1;
    protected $config;
    protected $control;
    public function __construct()
    {
        $this->config = (object) array(
            'appId' => '',
            'appSecret' => '',
            'partnerId' => '',
            'apiKey' => '',
            'notifyUrl' =>'https://dinghui.topdinghui.com/mobile/api/payment/wechatpay/notify_url.php',
          //  'finishedUrl' => WAP_SITE_URL . '/tmpl/member/payment_result.html?_=2&attach=_attach_',
          //  'undoneUrl' => WAP_SITE_URL . '/tmpl/member/payment_result_failed.html?_=2&attach=_attach_',
            'orderSn' => date('YmdHis'),
            'orderInfo' => 'Test wechatpay js api',
            'orderFee' => 1,
            'orderAttach' => '_',
        );
    }
    public function setConfig($name, $value)
    {
        $this->config->$name = $value;
    }
    public function setConfigs(array $params)
    {
        foreach ($params as $name => $value) {
            $this->config->$name = $value;
        }
    }
    public function notify()
    {
        try {
            $data = $this->onNotify();
            $resultXml = $this->arrayToXml(array(
                'return_code' => 'SUCCESS',
            ));
            if (self::DEBUG) {
                file_put_contents(__DIR__ . '/log.txt', var_export($data, true), FILE_APPEND | LOCK_EX);
            }
        } catch (Exception $ex) {
            $data = null;
            $resultXml = $this->arrayToXml(array(
                'return_code' => 'FAIL',
                'return_msg' => $ex->getMessage(),
            ));
            if (self::DEBUG) {
                file_put_contents(__DIR__ . '/log_err.txt', $ex . PHP_EOL, FILE_APPEND | LOCK_EX);
            }
        }
        return array(
            $data,
            $resultXml,
        );
    }

    public function onNotify()
    {
        $d = $this->xmlToArray(file_get_contents('php://input'));
        if (empty($d)) {
            throw new Exception(__METHOD__);
        }
        if ($d['return_code'] != 'SUCCESS') {
            throw new Exception($d['return_msg']);
        }
        if ($d['result_code'] != 'SUCCESS') {
            throw new Exception("[{$d['err_code']}]{$d['err_code_des']}");
        }
        if (!$this->verify($d)) {
            throw new Exception("Invalid signature");
        }
        return $d;
    }
    public function verify(array $d)
    {
        if (empty($d['sign'])) {
            return false;
        }
        $sign = $d['sign'];
        unset($d['sign']);
        return $sign == $this->sign($d);
    }
    public function paymentHtml($control,$openid) {
        try {
            $this->control = $control;
            $prepayId = $this->getPrepayId($openid);
            $params = array();
            $params['appId'] = $this->config->appId;
            $params['timeStamp'] = '' . time();
            $params['nonceStr'] = md5(uniqid(mt_rand(), true));
            $params['package'] = 'prepay_id=' . $prepayId;
            $params['signType'] = 'MD5';
            $sign = $this->sign($params);
            $params['paySign'] = $sign;
        } catch (Exception $ex) {
            $params['return_code']='FAIL';
            $params['return_msg']=$ex->getMessage();
        }
        return $params;


        $jsonParams = json_encode($params);

        return <<<EOB
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=utf-8" />
<title>微信安全支付</title>
</head>
<body>
正在加载…
<script type="text/javascript">
function jsApiCall() {
    WeixinJSBridge.invoke(
        'getBrandWCPayRequest',
        {$jsonParams},
        function(res) {
            var h;
            if (res && res.err_msg == "get_brand_wcpay_request:ok") {
                // success;
                h = '{$this->config->finishedUrl}';
            } else {
                // fail;
                alert(res && res.err_msg);
                h = '{$this->config->undoneUrl}';
            }
            location.href = h.replace('_attach_', '{$this->config->orderAttach}');
        }
    );
}
window.onload = function() {
    if (typeof WeixinJSBridge == "undefined") {
        if (document.addEventListener) {
            document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
        } else if (document.attachEvent) {
            document.attachEvent('WeixinJSBridgeReady', jsApiCall);
            document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
        }
    } else {
        jsApiCall();
    }
}
</script>
</body>
</html>
EOB;
    }
    public function getPrepayId($openid){
        $data = array();
        $data['appid'] = $this->config->appId;
        $data['mch_id'] = $this->config->partnerId;
        $data['nonce_str'] = md5(uniqid(mt_rand(), true));
        $data['body'] = $this->config->orderInfo;
        $data['attach'] = $this->config->orderAttach;
        $data['out_trade_no'] = $this->config->orderSn;
        $data['total_fee'] = $this->config->orderFee;
        $data['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $data['notify_url'] = $this->config->notifyUrl;
        $data['trade_type'] = 'JSAPI';
        $data['openid'] = $openid;
        $sign = $this->sign($data);
        $data['sign'] = $sign;
        $result = $this->postXml('https://api.mch.weixin.qq.com/pay/unifiedorder', $data);
        if ($result['return_code'] != 'SUCCESS') {
            throw new Exception($result['return_msg']);
        }
        if ($result['result_code'] != 'SUCCESS') {
            throw new Exception("[{$result['err_code']}]{$result['err_code_des']}");
        }
        return $result['prepay_id'];
    }

    public function sign(array $data){
        ksort($data);
        $a = array();
        foreach ($data as $k => $v) {
            if ((string) $v === '') {
                continue;
            }
            $a[] = "{$k}={$v}";
        }
        $a = implode('&', $a);
        $a .= '&key=' . $this->config->apiKey;
        return strtoupper(md5($a));
    }

    public function postXml($url, array $data) {
        $xml = $this->arrayToXml($data);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $response = curl_exec($ch);
        if (!$response) {
            throw new Exception('CURL Error: ' . curl_errno($ch));
        }
        curl_close($ch);
        return $this->xmlToArray($response);
    }
    public function arrayToXml(array $data){
        $xml = "<xml>";
        foreach ($data as $k => $v) {
            if (is_numeric($v)) {
                $xml .= "<{$k}>{$v}</{$k}>";
            } else {
                $xml .= "<{$k}><![CDATA[{$v}]]></{$k}>";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }
    public function xmlToArray($xml){//禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
}